using System.IO;
using System.Linq;
using System.Text;
using Dou.Scripts.Core.UI.Base;
using Dou.Scripts.Core.UI.Extensions;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using Button = UnityEngine.UI.Button;
using Slider = UnityEngine.UI.Slider;
using Toggle = UnityEngine.UI.Toggle;

public class UIGenerator
{
    public UIGenerator(UIGeneratorSettings setting, GameObject root)
    {
        this.setting = setting;
        view = root;
    }

    public UIGeneratorSettings setting;
    private GameObject view;

    private string[] oldFile = null;

    public void Generate(bool overrideOld = false)
    {
        if (setting != null)
        {
            string clsName = GetClassName(view);

            string file = setting.GenPath + '/' + clsName + ".cs";
            if (File.Exists(file))
                oldFile = File.ReadAllLines(file);
            StringBuilder text = new();
            GenUsingNameSpace(text);
            text.AppendLine("{");
            text.AppendLine($"\tpublic class {clsName} : {typeof(BaseView).FullName}");
            text.AppendLine("\t{");
            GenFields(text);
            GenAddListner(text);
            text.AppendLine("\t\t//#AUTO GEN END ========== write code below ==========");
            GenListnerMethod(text);
            bool isAUTO = false;
            if (oldFile != null)
            {
                for (int i = 0; i < oldFile.Length; i++)
                {
                    var l = oldFile[i];
                    if (l.StartsWith("\t\t//#AUTO GEN END") || l.Contains("//#AUTO GEN END"))
                        isAUTO = true;
                    else if (isAUTO)
                        text.AppendLine(l);
                }
            }

            if (!isAUTO)
            {
                text.AppendLine("\t}"); // clas
                text.AppendLine("}"); // ns
            }

            File.WriteAllText(file, text.ToString());
            AssetDatabase.Refresh();
        }

        Debug.Log("#AUTO GEN END");
    }


    bool IsFuncInOldFile(string fn)
    {
        if (oldFile != null)
        {
            foreach (var l in oldFile)
            {
                if (l.Contains(fn))
                    return true;
            }
        }

        return false;
    }

    private void GenListnerMethod(StringBuilder sb)
    {
        foreach (UIGeneratorSettings.ViewItem item in setting.viewItems)
        {
            string fN = GetListnerFuncName(item);
            if (IsFuncInOldFile($"{fN}("))
                continue;
            bool fn = true;
            if (item.component == typeof(Button).Name)
                sb.Append($"\t\tvoid {fN}()");
            else if (item.component == typeof(Slider).Name)
                sb.Append($"\t\tvoid {fN}(float v)");
            else if (item.component == typeof(Toggle).Name)
                sb.Append($"\t\tvoid {fN}(bool isOn)");
            else if (item.component == typeof(InputField).Name)
                sb.Append($"\t\tvoid {fN}(string txt)");
            else if (item.component == typeof(ButtonObject).Name)
                sb.Append($"\t\tvoid {fN}(Object obj)");
            else
                fn = false;
            if (fn)
                sb.AppendLine("{}");
        }
    }

    private void GenAddListner(StringBuilder sb)
    {
        sb.AppendLine("\t\tpublic void AddListener()\n\t\t{");
        foreach (UIGeneratorSettings.ViewItem item in setting.viewItems)
        {
            string fname = GetMemberName(item);
            if (item.component == typeof(Button).Name)
                sb.AppendLine($"\t\t\t{fname}.onClick.AddListener({GetListnerFuncName(item)});");
            if (item.component == typeof(Slider).Name)
                sb.AppendLine($"\t\t\t{fname}.onValueChanged.AddListener({GetListnerFuncName(item)});");
            if (item.component == typeof(Toggle).Name)
                sb.AppendLine($"\t\t\t{fname}.onValueChanged.AddListener({GetListnerFuncName(item)});");
            if (item.component == typeof(InputField).Name)
                sb.AppendLine($"\t\t\t{fname}.onValueChanged.AddListener({GetListnerFuncName(item)});");
            if (item.component == typeof(ButtonObject).Name)
                sb.AppendLine($"\t\t\t{fname}.SetOnClick({GetListnerFuncName(item)},{fname});");
        }

        sb.AppendLine("\t\t}");
    }

    private void GenFields(StringBuilder sb)
    {
        foreach (UIGeneratorSettings.ViewItem item in setting.viewItems)
        {
            sb.Append("\t\t[SerializeField] ");
            sb.AppendLine(item.component + " " + GetMemberName(item) + ";");
        }
    }

    private void GenUsingNameSpace(StringBuilder text)
    {
        text.AppendLine("using UnityEngine;");
        text.AppendLine("using UnityEngine.UI;");
        text.AppendLine("using TMPro;");
        text.AppendLine("using Dou.Scripts.Core.UI.Base;");
        text.AppendLine((string.IsNullOrEmpty(setting.UINameSpace) ? "" : "namespace " + setting.UINameSpace));
    }

    public static string GetClassName(GameObject item)
    {
        string className = item.name;
        if (PrefabUtility.IsAnyPrefabInstanceRoot(item))
        {
            var prefab = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(item);
            className = prefab.Split('.')[0].Split('/')[^1];
        }
        return GetForName(className, true);
    }

    public static string GetMemberName(UIGeneratorSettings.ViewItem item)
    {
        return GetForName(item.item.name, false);
    }

    public static string GetForName(string name, bool firstUperCase)
    {
        string n = name;
        var ns = n.Split(' ', '(', ')', '<', '>', '_', '-').ToList();
        n = string.Join("", ns);
        return (firstUperCase ? n.Substring(0, 1).ToUpper() : n.Substring(0, 1).ToLower()) +
               n.Substring(1, n.Length - 1);
    }

    public static string GetListnerFuncName(UIGeneratorSettings.ViewItem item)
    {
        string f = (item.component == "Button" || item.component == "ButtonObject")
            ? "Click"
            : "ValueChanged";
        return "On" + GetForName(item.item.name, true) + f;
    }
}